package com.black.cat.jvm.rtda.heap.constantpool;


import com.black.cat.jvm.classfile.constantpool.impl.ConstantInfoMethodref;
import com.black.cat.jvm.rtda.heap.methodarea.JClass;
import com.black.cat.jvm.rtda.heap.methodarea.JMethod;
import com.black.cat.jvm.rtda.heap.methodarea.MethodLookup;


public class MethodRef extends SymRef {

    private JMethod method;
    public String name;
    public String descriptor;

    public static MethodRef newMethodRef(RunTimeConstantPool runTimeConstantPool, ConstantInfoMethodref refInfo) {
        MethodRef ref = new MethodRef();
        ref.runTimeConstantPool = runTimeConstantPool;
        ref.copyMemberRefInfo(refInfo);
        return ref;
    }

    public void copyMemberRefInfo(ConstantInfoMethodref refInfo) {
        this.className = refInfo.getClassName();
        String[] split = refInfo.getNameAndType().split(":");
        this.name = split[0];
        this.descriptor = split[1];
    }

    public JMethod resolvedMethod() {
        if (null == this.method) {
            this.resolveMethodRef();
        }
        return this.method;
    }

    private void resolveMethodRef() {
        JClass d = this.runTimeConstantPool.getClazz();
        JClass c = this.resolvedClass();
        if (c.isInterface()) {
            throw new IncompatibleClassChangeError();
        }

        JMethod method = lookupMethod(c, this.name, this.descriptor);
        if (null == method){
            throw new NoSuchMethodError();
        }

        if (!method.isAccessibleTo(d)){
            throw new IllegalAccessError();
        }

        this.method = method;
    }

    public JMethod lookupMethod(JClass clazz, String name, String descriptor) {
        JMethod method = MethodLookup.lookupMethodInClass(clazz, name, descriptor);
        if (null == method) {
            method = MethodLookup.lookupMethodInInterfaces(clazz.interfaces, name, descriptor);
        }
        return method;
    }

    public String getName() {
        return name;
    }

    public String getDescriptor() {
        return descriptor;
    }
}
